// 日志器模块 -支持同步和异步两种模式——所以我们需要一个基类然后再派生出这两个日志系统
// 把前面的几个模块整合在一起，然后对外提供不同日志等级的接口
// 需要管理
// 格式化模块功能
// 落地对象模块数组
// 默认的输出等级
// 支持线程安全
// 日志器名称
// 需要提供的操作
// debug等级日志输出
// info等级日志输出
// warn等级日志输出
// fatail等级日志输出
// error等级日志输出
#pragma once
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "Mutex.hpp"
#include "lopper.hpp"
#include <atomic>
#include <mutex>
#include <stdarg.h>
#include <memory>
#include <functional>
#include <unordered_map>
using namespace std;

namespace LOG
{

    class Logger
    {
    public:
        using ptr = shared_ptr<Logger>;
        Logger(const string &logger_name, LogLevel::value level, Formatter::ptr formatter, vector<SinkLog::ptr> &sk)
            : _logger_name(logger_name),
              _level(level),
              _formatter(formatter),
              _sk(sk.begin(), sk.end())
        {
        }
        void DEBUG(size_t line, string name, const char *fmt, ...)
        {
            // 1 先判断是否可以通过日志等级
            if (_level > LogLevel::value::DEBUG)
            {
                return;
            }
            // 2 然后获取message的信息的主体
            va_list ptr;
            va_start(ptr, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, ptr); // 根据不同的类型读取一个个参数,失败返回-1
            if (ret != -1)
            {
            }
            va_end(ptr);
            // 3 然后构建msg
            LogMsg msg(LogLevel::value::DEBUG, line, name, _logger_name, res);
            // 4 然后进行格式化
            stringstream ss;
            _formatter->format(ss, msg);
            // 5 具体的落地方案
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void INFO(size_t line, string name, const char *fmt, ...)
        {
            // 1 先判断是否可以通过日志等级
            if (_level > LogLevel::value::INFO)
            {
                return;
            }
            // 2 然后获取message的信息的主体
            va_list ptr;
            va_start(ptr, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, ptr); // 根据不同的类型读取一个个参数,失败返回-1

            if (ret != -1)
            {
            }
            va_end(ptr);
            // 3 然后构建msg

            LogMsg msg(LogLevel::value::INFO, line, name, _logger_name, res);
            // 4 然后进行格式化
            stringstream ss;
            _formatter->format(ss, msg);
            std::cout << "info msg:[" << ss.str() << "]" << std::endl;
            // 5 具体的落地方案
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void WARN(size_t line, string name, const char *fmt, ...)
        {

            // 1 先判断是否可以通过日志等级
            if (_level > LogLevel::value::WARN)
            {
                return;
            }
            // 2 然后获取message的信息的主体
            va_list ptr;
            va_start(ptr, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, ptr); // 根据不同的类型读取一个个参数,失败返回-1
            if (ret != -1)
            {
            }
            va_end(ptr);
            // 3 然后构建msg
            LogMsg msg(LogLevel::value::WARN, line, name, _logger_name, res);
            // 4 然后进行格式化
            stringstream ss;
            _formatter->format(ss, msg);
            // 5 具体的落地方案
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void ERROR(size_t line, string name, const char *fmt, ...)
        {
            // 1 先判断是否可以通过日志等级
            if (_level > LogLevel::value::ERROR)
            {
                return;
            }
            // 2 然后获取message的信息的主体
            va_list ptr;
            va_start(ptr, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, ptr); // 根据不同的类型读取一个个参数,失败返回-1
            if (ret != -1)
            {
            }
            va_end(ptr);
            // 3 然后构建msg
            LogMsg msg(LogLevel::value::ERROR, line, name, _logger_name, res);
            // 4 然后进行格式化
            stringstream ss;
            _formatter->format(ss, msg);
            std::cout << "error msg:[" << ss.str() << "]" << std::endl;
            // 5 具体的落地方案
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void FATAL(size_t line, string name, const char *fmt, ...)
        {
            // 1 先判断是否可以通过日志等级
            if (_level > LogLevel::value::FATAL)
            {
                return;
            }
            // 2 然后获取message的信息的主体
            va_list ptr;
            va_start(ptr, fmt);
            char *res;
            int ret = vasprintf(&res, fmt, ptr); // 根据不同的类型读取一个个参数,失败返回-1
            if (ret != -1)
            {
            }
            va_end(ptr);
            // 3 然后构建msg
            LogMsg msg(LogLevel::value::FATAL, line, name, _logger_name, res);
            // 4 然后进行格式化
            stringstream ss;
            _formatter->format(ss, msg);
            // 5 具体的落地方案
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        const string getname()
        {
            return _logger_name;
        }

    protected:
        virtual void log(const char *data, size_t len) = 0;

    protected:
        string _logger_name;            // 日志器名称
        Mutex _mutex;                   // 互斥锁
        atomic<LogLevel::value> _level; // 日志等级
        Formatter::ptr _formatter;
        vector<SinkLog::ptr> _sk;
    };
    class SynLogger : public Logger
    {
    public:
        SynLogger(string &logger_name, LogLevel::value level, Formatter::ptr formatter, vector<SinkLog::ptr> &sk)
            : Logger(logger_name, level, formatter, sk)
        {
        }
        void log(const char *data, size_t len)
        {

            LockGuard logguard(&_mutex);
            if (_sk.empty() == true)
            {
                return;
            }
            for (auto &item : _sk)
            {
                cout << 2222 << endl;
                item->log(data, len);
            }
        }
    };
    // //我们使用日志器有很多的前置条件，要创建很多东西 ，比如如下图
    //  string logger_name = "SYNLOGGER";
    // LOG::LogLevel::value limit = LOG::LogLevel::value::WARN;
    // LOG::Formatter::ptr fmt = make_shared<LOG::Formatter>();
    // shared_ptr<LOG::SinkLog> std_out = LOG::SinkFactory::Create<LOG::StdoutSink>();
    // shared_ptr<LOG::SinkLog> file_out = LOG::SinkFactory::Create<LOG::FileSink>("./1/213/test.log");
    // vector<LOG::SinkLog::ptr> sinks = {std_out, file_out};
    // LOG::Logger::ptr loggerptr(make_shared<LOG::SynLogger> (logger_name,limit,fmt,sinks));
    // loggerptr->DEBUG(__LINE__,__FILE__,"%s","测试日志");

    // 所以我们需要建造着模式,我们其实并不需要指挥者，因为我们对顺序没什么要求，建造者模式主要对外提供统一接口
    // 抽象出一个日志器建造着类（完成日志器所需要的零部件的构造和日志器的构造）
    // 然后我们需要抽象出一个局部日志器建造着和全局日志器建造者（后面再弄）
    enum class LoggerType
    {
        SYNC_LOGGER,
        AYNS_LOGGER
    };

    class LoggerBulider
    {
    public:
        LoggerBulider()
            : _type(LoggerType::SYNC_LOGGER),
              _level(LogLevel::value::DEBUG) {}
        void setLoggerType(LoggerType type) { _type = type; }
        void setLoggerName(string logger_name) { _logger_name = logger_name; };
        void setLoggerLevel(LogLevel::value Level) { _level = Level; };
        void setLoggerFomatter(const string &partten)
        {
            _formatter = make_shared<Formatter>(partten);
        }
        template <typename Type, typename... Args>
        void setLoggerSinks(Args &&...args)
        {
            SinkLog::ptr sink = SinkFactory::Create<Type>(std::forward<Args>(args)...);
            _sinks.push_back(sink);
        }
        virtual shared_ptr<Logger> build() = 0;

    protected:
        LoggerType _type;
        string _logger_name;            // 日志器名称
        atomic<LogLevel::value> _level; // 日志等级
        Formatter::ptr _formatter;
        vector<SinkLog::ptr> _sinks;
    };
    class Asynlogger : public Logger
    {
    public:
        Asynlogger(string &logger_name, LogLevel::value level, Formatter::ptr formatter, vector<SinkLog::ptr> &sk)
            : Logger(logger_name, level, formatter, sk), _lopper(make_shared<AsyncLopper>(std::bind(&Asynlogger::relllog, this, placeholders::_1)))
        {
        }
        void log(const char *data, size_t len)
        {

            _lopper->push(data, len);
        }
        void relllog(Buffer &buffer)
        {

            LockGuard logguard(&_mutex);
            if (_sk.empty() == true)
            {
                return;
            }
            for (auto &item : _sk)
            {
                cout << "write size:" << buffer.readeAbleSize() << endl;

                item->log(buffer.begin(), buffer.readeAbleSize());
            }
        }

    private:
        shared_ptr<AsyncLopper> _lopper;
    };
    class LocalLoggerBulider : public LoggerBulider
    {
    public:
        Logger::ptr build()
        {
            assert(_logger_name.empty() == false);
            if (_formatter.get() == nullptr)
            {
                _formatter = make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                setLoggerSinks<StdoutSink>();
            }
            if (_type == LoggerType::AYNS_LOGGER)
            {
                return make_shared<Asynlogger>(_logger_name, _level, _formatter, _sinks);
            }
            return make_shared<SynLogger>(_logger_name, _level, _formatter, _sinks);
        }
    };

    // 异步日志器的实现-先把日志放到一个缓冲区里面，然后一个线程处理缓冲区里面的日志

    // 我们需要实现一个线程安全的缓冲区，可以设计一个队列结构，先进先出，不能使用链表，因为链表频繁对空间申请和释放->所以我们设计一个环形队列->
    // 这个过程设计到多线程，所以需要线程安全生产者和生产者之间的互斥，生产者和消费者之间的互斥->锁冲突很严重->所以提出了双缓冲区
    // 双缓冲区；业务线程写入任务写入缓冲区 ，然后异步工作线程从任务处理缓冲区提取，等写入缓冲区写满的话，交换写入缓冲区和任务处理缓冲区的指针->只有在
    // 交换的时候才有一次生产者和消费者的锁冲突
    // 还有要设计一个异步工作线程专门负责缓冲区中日志的落地
    // 以上在lopper.hpp里面实现

    // 异步日志器设计：
    //  1. 继承于Logger日志器类
    //  对于写日志操作进行函数重写（不再将数据直接写入文件，而是通过异步消息处理器，放到缓冲区中）
    //  2.通过异步消息处理器，进行日志数据的实际落地管理的成员：
    //  1. 异步工作器（异步消息处理器）
    //  完成后，完善日志器建造者，进行异步日志器安全模式的选择，提供异步日志器的创建

    class LoggerManger
    {
    public:
        static LoggerManger& Init()
        {
            static LoggerManger it;
            return it;
        }
        void add(Logger::ptr &logger)
        {
            if (find(logger->getname()))
            {
                return;
            }
            _map.insert(std::make_pair(logger->getname(), logger));
        }
        bool find(const string name)
        {
            auto ptr = _map.find(name);
            if (ptr == _map.end())
            {
                return false;
            }
            return true;
        }
        
        Logger::ptr getRoot()
        {
            return __root;
        }

    private:
        LoggerManger()
        {
            shared_ptr<LOG::LoggerBulider> _logger = make_shared<LOG::LocalLoggerBulider>();
            _logger->setLoggerName("root");
            LOG::Logger::ptr _ptr = _logger->build();
            _map.insert(std::make_pair("root", _ptr));
        }

    private:
        mutex _mutex;
        Logger::ptr __root;
        unordered_map<string, Logger::ptr> _map;
    };
}
